//-------------------------------------------------------------------
//	Copyright (c) 2010-2012  Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "openal.h"

#ifdef __cplusplus
extern "C" {
#endif
	

static	Engine::UXTypes::array< Engine::UXTypes::string >	_s_aLog;	
static	Engine::UXTypes::TLibrary							_s_sLib;
static	int													_s_iRefCounter = 0;


static const char * GetErrorString()
{
	static bool	bNeedDeleteLastString = false;

	if ( _s_aLog.Count() != 0 )
	{
		if ( bNeedDeleteLastString )
			_s_aLog.Erase( _s_aLog.Count()-1 );

		if ( _s_aLog.Count() == 0 )
			return nullptr;

		bNeedDeleteLastString = true;

		return _s_aLog.Back().cstr();
	}
	else
		return nullptr;
}

#define __ADD_TO_LOG( _text )			_s_aLog.PushFront( _text )

#define AL_GETPROCADDRESS( _address )	_s_sLib.GetProc( _address )


/* Init OpenAL Library */
#ifdef PLATFORM_WINDOWS
#	define OPENAL_INIT_LIB( _ret )		_ret = _s_sLib.Load( AL_LIBRARY_NAME )
#endif

#ifndef OPENAL_INIT_LIB
#	error not defined library initialization!
#endif


/* get OpenAL function address */
#if AL_DEBUG_LOG == 0
#	define OPENAL_GET_PROC( p, n, d ) \
		if ( (n = (p)AL_GETPROCADDRESS( #n )) == nullptr ) \
		{ \
			n = d; \
			ret &= false; \
		}
#else
#	define OPENAL_GET_PROC( p, n, d ) \
		if ( (n = (p)AL_GETPROCADDRESS( #n )) == nullptr ) \
		{ \
			n = d; \
			__ADD_TO_LOG( "Can't get address of function \""#n"\"." ); \
			ret &= false; \
		}
#endif	// AL_DEBUG_LOG




/* Create Default OpenAL functions */
#ifdef AL_USE_DEFAULT_FUNC

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
		AL_API _retType AL_API_CALL alDefault##_funcName _funcParams \
		{ \
			WARNING( #_funcName ); \
			return _retValue; \
		} \

	AL_FUNCTIONS
	AL_FUNCTIONS_EXT
	ALC_FUNCTIONS_EXT

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )

#endif	// AL_USE_DEFAULT_FUNC



/* AL Functions */
#undef  AL_BUILDFUNC
#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
	PFN##_funcName		_funcName	= nullptr;

AL_FUNCTIONS
AL_FUNCTIONS_EXT
ALC_FUNCTIONS_EXT

#undef  AL_BUILDFUNC
#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )



AL_API const char * AL_GetError()
{
	return GetErrorString();
}

AL_API void AL_ClearErrors()
{
	_s_aLog.Clear();
}
	
AL_API bool AL_Delete()
{
	if ( --_s_iRefCounter == 0 )
		return _s_sLib.Free();
	else
		return true;
}

AL_API char AL_Init()
{
	static bool		s_bInited = false;
	
	++_s_iRefCounter;

	if ( s_bInited )
		return 0;	// success

	bool	ret = true;

	OPENAL_INIT_LIB( ret );

	if ( !ret ) {
		__ADD_TO_LOG( "Error: can't find OpenAL library." );
		return 2;	// fail
	}


	// version 1.0 //

	#undef  AL_BUILDFUNC
	#ifdef AL_USE_DEFAULT_FUNC
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, &alDefault##_funcName );
	#else
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, nullptr );
	#endif	// AL_USE_DEFAULT_FUNC

	AL_FUNCTIONS

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )


	s_bInited = true;
	return ret ? 0 : 1;		// success or with errors
}


AL_API bool AL_InitExtensions(void *pALCDevice)
{
	bool		ret		= true;
	ALCdevice *	device	= (ALCdevice *)pALCDevice;

	// version 1.1 //
		
	#undef  AL_BUILDFUNC
	#ifdef AL_USE_DEFAULT_FUNC
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, &alDefault##_funcName );
	#else
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, nullptr );
	#endif	// AL_USE_DEFAULT_FUNC
	

	#undef  AL_GETPROCADDRESS
	#define AL_GETPROCADDRESS( _address )	alGetProcAddress( _address )

	AL_FUNCTIONS_EXT

	#undef  AL_GETPROCADDRESS
	#define AL_GETPROCADDRESS( _address )	alcGetProcAddress( device, _address )

	ALC_FUNCTIONS_EXT

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )

	return ret;
}


#undef	__ADD_TO_LOG
#undef	AL_GETPROCADDRESS
#undef	OPENAL_INIT_LIB
#undef	OPENAL_GET_PROC


#ifdef __cplusplus
}
#endif
